//Section Constructor
function Section(key,world) {
	this.Key = key;
	this.Map = new Array();
	this.World = world;
	this.Exits = new Object();
	this.NPCs = new Array();
	
	//Directions Array Setup for BLOS
	var dirs = new Array(3);
	dirs[-1] = new Array(2);
	dirs[0] = new Array(2);
	dirs[1] = new Array(2)
	dirs[-1][-1]=7;
	dirs[-1][1]=1;
	dirs[0][-1]=4;
	dirs[0][1]=6;
	dirs[1][-1]=9;
	dirs[1][1]=3;
	
	this.Dirs=dirs;
}

Section.prototype.Save = function () {
	//Save Section to String.
	//***TODO***
	var saveObject = new Object();
	saveObject.type="Section";
	saveObject.Key = this.Key;
	saveObject.Map = this.Map;
	saveObject.Exits = this.Exits;
	return JSON.stringify(saveObject);
};

Section.prototype.Load = function (str) {
	//Load Section from String.
	//***TODO***
	var saveObject = eval("("+str+")");
	this.Key = saveObject.Key;
	this.Map = saveObject.Map;
	this.Exits = saveObject.Exits;
};

Section.prototype.genBlank = function () {
	this.Map = new Array(24);
	var i=0,j=0;
	for(i=0;i<24;i++) {
		this.Map[i] = new Array(80);
		for(j=0;j<80;j++) {
			this.Map[i][j] = new Array(4);
			this.Map[i][j][0] = [1,0];
			this.Map[i][j][1] = -1;
			this.Map[i][j][2] = new Array();
			this.Map[i][j][3] = 1; //0 (Not Discovered), 1 (Visible) 
		}
	}
};

Section.prototype.genForest = function(thick) {
	this.Map = new Array(24);
	var n=0,s=0,e=0,w=0;
	n=Math.floor(Math.random()*78)+1;
	s=Math.floor(Math.random()*77)+1;
	e=Math.floor(Math.random()*22)+1;
	w=Math.floor(Math.random()*22)+1;
	for(var i=0;i<24;i++) {
		this.Map[i] = new Array(80);
		for(var j=0;j<80;j++) {
			this.Map[i][j] = new Array(4);
			this.Map[i][j][0] = new Array(2);
			this.Map[i][j][0][0] = 0;
			this.Map[i][j][0][1] = 0;
			
			//Place Exits, Then Trees, Then Ground.
			if ((i==0) && (j==n)) {
				this.Map[i][j][0][0] = 21;
				this.addExit("N",i,j);
			} else if ((i==23) && (j==s)) {
				this.Map[i][j][0][0] = 22;
				this.addExit("S",i,j);
			} else if ((i==w) && (j==0)) {
				this.Map[i][j][0][0] = 24;
				this.addExit("W",i,j);
			} else if ((i==e) && (j==79)) {
				this.Map[i][j][0][0] = 23;
				this.addExit("E",i,j);
			} else if (Math.random() < thick) {
				if (Math.random()<.75) {
					this.Map[i][j][0][0]=51;
				} else {
					this.Map[i][j][0][0]=50;
				}
			} else {
				if (Math.random() >= .5) this.Map[i][j][0][0] = 1;
				else this.Map[i][j][0][0] = 2;
				
			}
			
			this.Map[i][j][1] = -1;
			this.Map[i][j][2] = new Array(); //Item Array
			this.Map[i][j][3] = 0; //0 (Not Discovered), 1 (Visible) 
		}
	}
}

Section.prototype.genForestCabin = function() {
	var height = Math.floor(Math.random()*4)+5;
	var width = Math.floor(Math.random()*4)+5;
	var door = Math.floor(Math.random()*4);
	var xPos = Math.floor(Math.random()*(77-width))+2;
	var yPos = Math.floor(Math.random()*(21-height))+2;
	if (door % 2) var doorpos = Math.floor(Math.random()*(width-3)) +1;
	else var doorpos = Math.floor(Math.random()*(height-3))+1;
	
	for (var i=yPos-1;i<yPos+height+1;i++) {
		for (var j=xPos-1;j<xPos+width+1;j++) {
			this.Map[i][j][0][0] = 3;
			if (Math.random() < .5) this.Map[i][j][0][0] = 4;
			if ((i==yPos || i==(yPos+height-1)) && (j>=xPos && j<=(xPos+width-1))) {
				this.Map[i][j][0][0] = 30;
			} else if ((j==xPos || j==(xPos+width-1)) && (i>=yPos && i<=(yPos+height-1))) {
				this.Map[i][j][0][0] = 30;
			}
		}
	}
	
	if (door==0) this.Map[yPos][xPos+doorpos][0][0] = 11;
	if (door==1) this.Map[(yPos+height-1)][(xPos+doorpos)][0][0] = 11;
	if (door==2) this.Map[yPos+doorpos][xPos][0][0] = 11;
	if (door==3) this.Map[yPos+doorpos][xPos+width-1][0][0] = 11;
}

Section.prototype.genCave = function(open) {
	this.Map = new Array(24);
	var hex=new Array(2),tot,dir=[6,7,4,1,3,6];
	for(var i=0;i<24;i++) {
		this.Map[i] = new Array(80);
		for(var j=0;j<80;j++) {
			this.Map[i][j] = new Array(4);
			this.Map[i][j][0] = new Array(2);
			this.Map[i][j][0][0] = 32;
			if (Math.random() < .2) this.Map[i][j][0][1] = 31;
			if (!(i==0 || j==0 || i==23 || j==79)) {
				if (Math.random() < open) {
					if (Math.random() < .5) {
						this.Map[i][j][0][0] = 5;
					} else {
						this.Map[i][j][0][0] = 6;
					}
				}
			}
			
			this.Map[i][j][1] = -1;
			this.Map[i][j][2] = new Array(); //Item Array
			this.Map[i][j][3] = 0; //0 (Not Discovered), 1 (Visible) 
		}
	}
	
	//Cellular Automata
	for(var i=1;i<23;i++) {
		for(var j=1;j<79;j++) {
			tot=0;
			hex[0]=i;
			hex[1]=j;
			for(var z=0;z<6;z++) {
				hex = this.nextHex(hex,dir[z]);
				if (this.Map[hex[0]][hex[1]][0][0]>0 && this.Map[hex[0]][hex[1]][0][0]<7) tot += 1;
			}
			
			if (tot == 0) {
				if (Math.random() < .5) this.Map[i][j][0][0] = 31;
				else this.Map[i][j][0][0] = 32;
			}
			if (tot > 3 && tot < 6) {
				if (Math.random() < .5) this.Map[i][j][0][0] = 5;
				else this.Map[i][j][0][0] = 6;
			}
		}
	}
}

Section.prototype.addExit = function(key,y,x) {
	this.Exits[key] = new Array(2);
	this.Exits[key][0] = y;
	this.Exits[key][1] = x;
}

Section.prototype.walkable = function(y,x) {
	return this.World.Display.TileWlk[this.Map[y][x][0][0]];
}

Section.prototype.exitDirection = function(y,x) {
	if (this.Map[y][x][0][0] == 24) return "W";
	if (this.Map[y][x][0][0] == 23) return "E";
	if (this.Map[y][x][0][0] == 21) return "N";
	if (this.Map[y][x][0][0] == 22) return "S";
	return "";
}

Section.prototype.updateDisplay = function () {
	var i=0,j=0;
	
	this.hexFOV(this.World.Player.yPos,this.World.Player.xPos,25,this.addVisibleHex);
	
	//Update Display
	for (i=0;i<24;i++) {
		for(j=0;j<80;j++) {
			//If the hex is visible.
			if (this.Map[i][j][3]==1) {
				//If there is a creature visible map it.
				//Else show top item on stack.
				//else show floor.
				if (this.Map[i][j][1]!=-1) {
					if (this.Map[i][j][1]=="P") {
						this.World.Display.Screen[i][j][0]=this.World.Player.Display;
						this.World.Display.Screen[i][j][1]=this.World.Player.Color;
					} else {
						this.World.Display.Screen[i][j][0]=this.NPCs[this.Map[i][j][1]].Display;
						this.World.Display.Screen[i][j][1]=this.NPCs[this.Map[i][j][1]].Color;
					}
				} else if (this.Map[i][j][2].length > 0) {
					//Display Top Item.
					//***TODO***
				} else {
					this.World.Display.setScreen(i,j,this.Map[i][j][0][0]);
				}
			} else {
				this.World.Display.Screen[i][j][0] = "&nbsp;";
			}			
		}
	}
};

Section.prototype.addVisibleHex = function(hex,sect) {
	sect.Map[hex[0]][hex[1]][3]=1;
}

Section.prototype.openClose = function(dir,open) {
	dir = parseInt(dir);
	var hex = new Array(2);
	hex[0] = this.World.Player.yPos;
	hex[1] = this.World.Player.xPos;
	hex = this.nextHex(hex,dir);
	
	if (open) {
		if (this.Map[hex[0]][hex[1]][0][0]!=11) return 1;
		//Change to open door.
		this.Map[hex[0]][hex[1]][0][0]=10;
	} else {
		if (this.Map[hex[0]][hex[1]][0][0]!=10) return 2;
		//Chage to closed door.
		this.Map[hex[0]][hex[1]][0][0]=11;
	}
	
	this.updateDisplay();
	return 0;
	
}

//Return a list of currently visible hexs from the position on the map given.
Section.prototype.hexFOV = function(y,x,range,func) {
	var hex = new Array();
	var hexcenter;
	hex[0]=y;
	hex[1]=x;
	//Preform the function on the center hex.
	func(hex,this);
	var center = this.hexCenter(y,x);
	var shadows = new Array();
	var r=0;
	var st=0;
	var rc=0;
	var incst=false;
	var hexes = new Array();
	hexes[0] = hex;
	//Spiral Outwards
	while(r < range) {
		//Move outwards
		switch(st) {
		case 0:
			hex=this.nextHex(hex,6);
			incst=true;
			r++;
			break;
		case 1:
			if (rc == (r-1)) {
				st+=1;
				break;
			}
			hex=this.nextHex(hex,9);
			rc++;
			if (rc == (r-1)) incst=true;
			break;
		case 2:
			hex=this.nextHex(hex,7);
			rc++;
			if (rc == r) incst=true;
			break;
		case 3:
			hex=this.nextHex(hex,4);
			rc++;
			if (rc == r) incst=true;
			break;
		case 4:
			hex=this.nextHex(hex,1);
			rc++;
			if (rc == r) incst=true;
			break;
		case 5:
			hex=this.nextHex(hex,3);
			rc++;
			if (rc == r) incst=true;
			break;
		case 6:
			hex=this.nextHex(hex,6);
			rc++;
			if (rc == r) incst=true;
			break;
		}
		
		//Increment the state if we are at that point.
		if (incst) {
			rc=0;
			st++;
			st%=7;
			incst=false;
		}
		
		//Skip checks if the hex isn't on the grid.
		if (hex[0]<0) continue;
		if (hex[1]<0) continue;
		if (hex[0]>23) continue;
		if (hex[1]>79) continue;
		
		hexcenter = this.hexCenter(hex[0],hex[1]);
		var base = Math.atan2(hexcenter[0]-center[0],hexcenter[1]-center[1]);
		var vis=true;
		for(var i=0;(i<shadows.length)&&vis;i++) {
			if ((shadows[i][0] < base) && (shadows[i][1] > base)) vis = false;
		}
		if (vis) {
			func(hex,this);
		}
		
		
		//Check the hex for being a obstacle
		if (!this.walkable(hex[0],hex[1])) {
			var shadow = this.obstructedAngles(hexcenter,center,base);
			if (shadow[1] > Math.PI) {
				shadows[shadows.length] = new Array(2);
				shadows[shadows.length-1][0] = -Math.PI;
				shadows[shadows.length-1][1] = shadow[1]-(2*Math.PI);
			}
			shadows[shadows.length] = shadow;
		}
	}
	
	return hexes;
}

Section.prototype.obstructedAngles = function(hex,cen,base) {
	//Cirle Radius to determine hex's shadow.
	//Slightly larger than touching circle tangential with hexes edges.
	var r = .44;
	var angles = new Array();
	var d = this.distance(hex,cen);
	angles[0] = base - Math.asin(r/d);
	angles[1] = base + Math.asin(r/d);
	return angles;
}

Section.prototype.nextHex = function(hex,dir) {
	if (dir==6) {
		hex[1]++;
	} else if (dir==9) {
		hex[0]--;
		hex[1]++;
	} else if (dir==7) {
		hex[0]-=1;
	} else if (dir==4) {
		hex[1]-=1;
	} else if (dir==1) {
		hex[0]+=1;
	} else if (dir==3) {
		hex[0]+=1;
		hex[1]+=1;
	}
	
	//Handle the odd rows
	if ((hex[0]%2) && (dir>6 || dir < 4)) {hex[1]--;}
	
	return hex;
}

//Get the center point of a hex.
Section.prototype.hexCenter = function(y,x) {
	var center = new Array();
	center[0] = .75 * y;
	center[1] = .866025 * x;
	if (y%2) center[1]+=.4330125;
	return center;
}

Section.prototype.placePlayer = function (creature,ypos,xpos) {
	if (!this.walkable(ypos,xpos)) return false;
	if (creature==0) {this.Map[ypos][xpos][3]=1;}
	if (creature=="P") {
		this.World.Player.Section = this;
		this.World.Player.yPos=ypos;
		this.World.Player.xPos=xpos;
		this.Map[ypos][xpos][1]="P";
	} else {
		this.NPCs[creature].Section = this;
		this.NPCs[creature].yPos=ypos;
		this.NPCs[creature].xPos=xpos;
		this.Map[ypos][xpos][1]=creature;
	}
	return true;
}

Section.prototype.distance = function (c1,c2) {
	return Math.sqrt(Math.pow(c1[0]-c2[0],2) + Math.pow(c1[1]-c2[1],2));
}

Section.prototype.BLOS = function(start,end,process) {
	var dx=end[1]-start[1],dy=end[0]-start[0],ddx=2*dx;
	var sx=1,sy=1;
	var ax=Math.abs(ddx),ay=Math.abs(dy);
	var cur = new Array(2);
	
	if (dx < 0) sx=-1;
	if (dy < 0) sy=-1;
	
	//Set starting point
	cur[0]=start[0];
	cur[1]=start[1];
	
	//Process the current node
	process(this,cur);
	var e=-2*ax;
	while((cur[0]!=end[0])||(cur[1]!=end[1])) {
		if (e >= 0) {
			cur=this.nextHex(cur,this.Dirs[-sx][sy]);
			e=e-3*ay-3*ax;
		} else {
			e=e+3*ay;
			if (e > -ax) {
				cur=this.nextHex(cur,this.Dirs[sx][sy]);
				e=e-3*ax;
			} else {
				if (e < -3*ax) {
					cur=this.nextHex(cur,this.Dirs[sx][-sy]);
					e=e+3*ax;
				} else {
					cur=this.nextHex(cur,this.Dirs[0][sx]);
					e=e+3*ay;
				}
			}
		}
		process(this,cur);
	}
}